
package com.crl.ui.bitmaps
{
	import com.crl.core.IDispose;
	import com.crl.utils.timer.FrameRate;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	[Event(name="enterFrame", type="com.hexagonstar.event.FrameEvent")]
	

	public class AnimatedBitmap extends Bitmap implements IDispose
	{

		protected var _buffer:BitmapData;

		protected var _fpsTimer:FrameRate;

		protected var _totalFrames:int;

		protected var _currentFrame:int;

		protected var _isPlaying:Boolean;

		protected var _point:Point;

		protected var _rect:Rectangle;

		protected var _frameWidth:int;

		protected var _resetWidth:int;
		
		public function AnimatedBitmap(bitmap:BitmapData,
									   frameWidth:int,
									   fps:int,
									   transparent:Boolean = true,
									   pixelSnapping:String = "auto",
									   smoothing:Boolean = false
		)
		{
			var h:int = bitmap.height;
			super(new BitmapData(frameWidth, h, transparent, 0x00000000), pixelSnapping,smoothing);
			
			_buffer = bitmap.clone();
			_totalFrames = _buffer.width / frameWidth;
			_currentFrame = 1;
			_isPlaying = false;
			
			this._fpsTimer=new FrameRate(fps);
				
			_point = new Point(0, 0);
			_rect = new Rectangle(0, 0, frameWidth, h);
			_frameWidth = frameWidth;
			_resetWidth = -(_buffer.width - frameWidth);
			
			bitmapData.copyPixels(_buffer, _rect, _point);
		}
		
		
		/**
		 * Starts the playback of the animated bitmap. If the animated bitmap is already
		 * playing while calling this method, it calls stop() and then play again instantly
		 * to allow for framerate changes during playback.
		 */
		public function play():void
		{
			// TODO add reverse playback functionality.
			if (!_isPlaying)
			{
				_isPlaying = true;
				_fpsTimer.addEventListener(TimerEvent.TIMER, onEnterFrame);
				_fpsTimer.start();
			}
			else
			{
				stop();
				play();
			}
		}
		
		
		/**
		 * Stops the playback of the animated bitmap.
		 */
		public function stop():void
		{
			if (_isPlaying)
			{
				_fpsTimer.stop();
				_fpsTimer.removeEventListener(TimerEvent.TIMER, onEnterFrame);
				_isPlaying = false;
			}
		}
		
		
		/**
		 * Jumps to the specified frame nr. and plays the animated bitmap from that
		 * position. Note that the frames of an animated bitmap start at 1 just like
		 * a MovieClip.
		 * 
		 * @param frame an Integer that designates the frame from which to start play.
		 * @param scene unused.
		 */
		public function gotoAndPlay(frame:Object, scene:String = null):void
		{
			_currentFrame = int(frame) - 1;
			play();
		}
		
		
		/**
		 * Jumps to the specified frame nr. and stops the animated bitmap at that position.
		 * Note that the frames of an animated bitmap start at 1 just like a MovieClip.
		 * 
		 * @param frame an Integer that designates the frame to which to jump.
		 * @param scene unused.
		 */
		public function gotoAndStop(frame:Object, scene:String = null):void
		{
			var f:int = int(frame);
			if (f >= _currentFrame)
			{
				_currentFrame = f - 1;
				nextFrame();
			}
			else
			{
				_currentFrame = f + 1;
				prevFrame();
			}
		}
		
		
		/**
		 * Moves the animation to the next of the current frame. If the animated bitmap is
		 * playing, the playback is stopped by this operation.
		 */
		public function nextFrame():void
		{
			if (_isPlaying) stop();
			_currentFrame++;
			if (_currentFrame > _totalFrames) _currentFrame = _totalFrames;
			draw();
		}
		
		
		/**
		 * Moves the animation to the previous of the current frame. If the animated bitmap
		 * is playing, the playback is stopped by this operation.
		 */
		public function prevFrame():void
		{
			if (_isPlaying) stop();
			_currentFrame--;
			if (_currentFrame < 1) _currentFrame = 1;
			draw();
		}
		
		
		/**
		 * Disposes the animated bitmap.
		 */
		public function dispose():void
		{
			stop();
		}
		
		
		public function set fps(_fps:uint):void
		{
			
			if (_isPlaying)
			{
				stop();
				_fpsTimer.fps=_fps;
				play();
			}
			else
			{
				_fpsTimer.fps =_fps;
			}
		}
		
		
		/**
		 * Returns the frame rate with that the animated bitmap is playing.
		 * 
		 * @return The fps value of the animated bitmap.
		 */
		public function get frameRate():int
		{
			return _fpsTimer.frameRate;
		}
		
		
		/**
		 * Returns the current frame position of the animated bitmap.
		 * 
		 * @return The current frame position.
		 */
		public function get currentFrame():int
		{
			return _currentFrame;
		}
		
		
		/**
		 * Returns the total amount of frames that the animated bitmap has.
		 * 
		 * @return The total frame amount.
		 */
		public function get totalFrames():int
		{
			return _totalFrames;
		}
		
		
		/**
		 * Returns whether the animated bitmap is playing or not.
		 * 
		 * @return true if the animated bitmap is playing, else false.
		 */
		public function get isPlaying():Boolean
		{
			return _isPlaying;
		}
		
		/**
		 * Plays the animation forward by one frame.
		 * @private
		 */
		protected function onEnterFrame(e:TimerEvent):void
		{
			_currentFrame++;
			if (_currentFrame > _totalFrames)
			{
				_currentFrame = 1;
			}
			else if (_currentFrame < 1)
			{
				_currentFrame = _totalFrames;
			}
			draw();
		}
		
		
		////////////////////////////////////////////////////////////////////////////////////////
		// Private Methods                                                                    //
		////////////////////////////////////////////////////////////////////////////////////////
		
		/**
		 * Draws the next bitmap frame from the buffer to the visible area.
		 * @private
		 */
		protected function draw():void
		{
			_rect.offset(_currentFrame == 1 ? _resetWidth : _frameWidth, 0);
			bitmapData.copyPixels(_buffer, _rect, _point);
			
			dispatchEvent(new Event(Event.ENTER_FRAME));
		}
	}
}
